Define engine-to-integrator error contract#43
Merged
anormang1992 merged 1 commit intomainfrom Mar 30, 2026
Merged
Conversation
000bc8d to
6ea6544
Compare
There was a problem hiding this comment.
Pull request overview
Defines a typed, integrator-facing error contract across the VRE stack, ensuring engine/repository/grounding layers re-raise consistent VREError subclasses (instead of raw/untyped exceptions) and avoid silently overriding integrator decisions.
Changes:
- Introduces
vre.core.errorsexception hierarchy and re-exports error types fromvre.coreand top-levelvre. - Updates learning engine validation + cycle handling to raise typed exceptions and propagate
CyclicRelationshipError(with in-memory rollback). - Wraps Neo4j repository operations and hydration to prevent raw driver/JSON exceptions from leaking to integrators; updates policy cardinality handling to “safe default” behavior.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
src/vre/core/errors.py |
Adds new VRE exception hierarchy. |
src/vre/core/__init__.py |
Re-exports new error types from vre.core. |
src/vre/__init__.py |
Re-exports error types from top-level package; adjusts cardinality parsing to safe-default behavior. |
src/vre/core/graph.py |
Wraps Neo4jError at repository boundary and wraps hydration failures with HydrationError. |
src/vre/core/grounding/resolver.py |
Uses ResolutionError when spaCy model is missing. |
src/vre/learning/engine.py |
Replaces ValueError with CandidateValidationError; propagates CyclicRelationshipError after rollback. |
src/vre/core/policy/models.py |
Wraps policy callback dotted-path resolution failures as VREError. |
src/vre/core/policy/gate.py |
Allows cardinality=None to mean “unknown → do not skip any policies.” |
src/vre/core/models.py |
Removes CyclicRelationshipError from models (moved to errors module). |
tests/vre/test_learning.py |
Updates tests for typed learning validation errors and cycle propagation behavior. |
tests/vre/test_vre.py |
Updates tests for unknown-cardinality safe-default policy behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Establish a typed exception hierarchy and consistent error contract so integrators never see raw ValueError/Neo4j exceptions. VRE rolls back in-memory mutations and re-raises — integrators decide recovery strategy. Key changes: - New core/errors.py with VREError hierarchy (GraphError, PersistenceError, GraphIntegrityError, CyclicRelationshipError, HydrationError, ResolutionError, CandidateValidationError) - Fix silent override: CyclicRelationshipError in _persist_reachability now does rollback + re-raise instead of silently returning SKIPPED - Wrap all PrimitiveRepository methods with GraphError/PersistenceError - Wrap hydration with HydrationError, resolver with ResolutionError - Unknown cardinality defaults to None, firing all policies (safe default) - CyclicRelationshipError re-parented from ValueError to GraphIntegrityError Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6ea6544 to
2179d86
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #42.
core/errors.py— typed exception hierarchy rooted atVREError. Integrators can catch at desired granularity (individual error type →VREErrorcatch-all).CyclicRelationshipErrorin_persist_reachabilitynow does rollback + re-raise instead of silently converting toSKIPPED. The engine cleans up its in-memory mutation; the integrator decides recovery strategy.PrimitiveRepositorymethods catchNeo4jErrorand raiseGraphError(reads) orPersistenceError(writes). Integrators never see raw Neo4j driver exceptions.HydrationErrorinstead of rawjson.JSONDecodeError/KeyError.None, which fires all policies. If we can't determine the cardinality, we can't justify skipping any policy.CyclicRelationshipErrorre-parented fromValueErrortoGraphIntegrityError(VREError). Breaking change — clean break, no backward-compat shim.Error hierarchy
Test plan
CyclicRelationshipErrorto propagate (notSKIPPED)BLOCKCandidateValidationErrorreplacesValueErrorin all learning engine validationImplementation plan
Issue #42: Engine-to-Integrator Error Contract
Context
The LearningEngine silently overrides integrator decisions when post-decision failures occur (
engine.py:279-283). More broadly, VRE lacks a consistent error contract — many errors propagate as rawValueErroror untyped exceptions, making it hard for integrators to handle them programmatically.Principle: VRE's responsibility is to roll back any in-memory mutations and re-raise errors with clear, typed exceptions. Integrators decide recovery strategy. VRE does not make recovery decisions on behalf of integrators.
Guard decorator: Unhandled exceptions propagating out of
vre_guardis acceptable — the integrator wraps the decorated function call in their own error handling.Part 1: Error Hierarchy — new
src/vre/core/errors.pyDedicated module for the exception hierarchy. Keeps
models.pyfocused on domain models.CyclicRelationshipErrormoves fromcore/models.pytocore/errors.py. Clean break — no re-export frommodels.py.ValueErrortoGraphIntegrityError(VREError). Breaking change — acceptable.from vre.core.errors import CyclicRelationshipError(orfrom vre import CyclicRelationshipError).Part 2: Fix the Silent Override
File:
src/vre/learning/engine.py:279-283Exception propagates through
learn_at→learn_all→ integrator. Thewith callback:context manager ensures__exit__fires for cleanup.Part 3: Wrap Critical Sections
3a. Repository boundary — Neo4j (
src/vre/core/graph.py)Wrap all public methods so integrators never see raw Neo4j exceptions. Use
GraphErrorfor reads,PersistenceErrorfor writes:save_primitive()PersistenceError(letCyclicRelationshipError+ provenanceValueErrorpass through)delete_primitive()PersistenceErrorfind_by_id()GraphErrorfind_by_name()GraphErrorlist_names()GraphErrorresolve_subgraph()GraphErrorensure_constraints()GraphError3b. Hydration (
src/vre/core/graph.py)Wrap
_hydrate_primitivewithHydrationErrorfor JSON/data corruption.3c. Concept resolution (
src/vre/core/grounding/resolver.py)Replace
RuntimeErrorwithResolutionErrorfor missing spaCy model.3d. Learning engine validation (
src/vre/learning/engine.py)Replace raw
ValueErrorwithCandidateValidationErrorin all_persist_*methods,_resolve_name_to_id, andlearn_at.3e. Policy callback resolution (
src/vre/core/policy/models.py)Wrap
resolve_callback()withVREErrorforImportError/AttributeError.3f. Cardinality — safe default on bad input
Default to
Noneon unknown cardinality. Update the gate to fire all policies when cardinality is unknown — if we can't determine cardinality, we can't justify skipping any policy.🤖 Generated with Claude Code